/* 
* Copyright (c) 2021 Talkweb Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License. 
*/

#include <stdio.h>
#include <unistd.h>
#include "ohos_init.h"
#include "cmsis_os2.h"
#include "lwip/sockets.h"
#include "wifi_connect.h"

#include<sys/types.h>
#include<sys/select.h>
#include<netinet/in.h>

/*TCP SERVER 参数*/
#define _PROT_ 8800
#define TCP_BACKLOG 5
#define _SERVER_IP_ "192.168.1.112"
#define IP_LEN 16

/*wifi参数配置*/
#define WIFI_SSID "aaa"
#define WIFI_PASSWORD "talkweb1996"

/*接收缓存和数据*/
char recvbuf[1460];
char *tcpSendBuf = "Hello! I'm Talkweb TCP Server!";


#define _MAX_SIZE_ 10
int fd_arr[_MAX_SIZE_];
int max_fd=0;
static void Useage(const char* proc)
{
	printf("Useage:%s,[ip][port]\n");
	exit(1);
}
static int add_fd_arr(int fd)
{
	//fd add to fd_arr
	int i=0;
	for(;i<_MAX_SIZE_;++i)
	{
		if(fd_arr[i]==-1)
		{
			fd_arr[i]=fd;
			return 0;
		}
	}
	return 1;
}


/*tcp服务器任务，采用select*/
static void TCPServerTaskWithSelect(void)
{
	//服务端地址信息
	struct sockaddr_in server_sock;
	//客户端地址信息
	struct sockaddr_in client_sock;
	int sin_size = sizeof(struct sockaddr_in);
	struct sockaddr_in *cli_addr;
	//连接Wifi
	if(WifiConnect(WIFI_SSID,WIFI_PASSWORD)!=0)
	{
		perror("Wifi connect error!");
		exit(1);
	}
	//in_addr_t localIpaddr = GetLocalIpaddr();
	//printf("ip = %x\n",localIpaddr);

	//创建socket
	//printf("socket begin\n");
	int sock_fd=-1,new_fd=-1;
	if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket is error\r\n");
		exit(1);
	}
	//printf("sock_fd=%d\n",sock_fd);

	bzero(&server_sock, sizeof(server_sock));
	server_sock.sin_family = AF_INET;
	//server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
	inet_pton(AF_INET, _SERVER_IP_, &server_sock.sin_addr);
	server_sock.sin_port = htons(_PROT_);
	char str[IP_LEN];
	inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str));
	printf("tcp server IP_addr: %s at PORT %d\n",str,ntohs(server_sock.sin_port));
	
	//调用bind函数绑定socket和地址
	if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1)
	{
		perror("bind is error\r\n");
		exit(1);
	}
	//调用listen函数监听(指定port监听)
	if (listen(sock_fd, TCP_BACKLOG) == -1)
	{
		perror("listen is error\r\n");
		exit(1);
	}
	//init fd_arr
	//printf("start select\r\n");
	int i=0;
	for(;i<_MAX_SIZE_;++i)
	{
		fd_arr[i]=-1;
	}
	add_fd_arr(sock_fd);
	fd_set fds;
	FD_ZERO(&fds);

	while(true)
	{
		//更新max_fd和文件描述符集
		max_fd=sock_fd;
		for(int ii=0; ii<_MAX_SIZE_; ii++)
		{
			if(fd_arr[ii]!=-1)
			{
				FD_SET(fd_arr[ii],&fds);
				if(fd_arr[ii]>max_fd)
				{
					max_fd=fd_arr[ii];
				}
			}
		}
		//printf("update max_fd = %d\n",max_fd);
		printf("select wait...\n");
        int ret = select(max_fd + 1, &fds, NULL, NULL, NULL);
		if(ret<=0){
			//超时等
			printf("[DISCOVERY]ret:%d\n", ret);
			continue;
		}
		if(FD_ISSET(sock_fd,&fds)) //判断是否发生accept
		{
			new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size);
			//printf("accept new fd = %d\n",new_fd);
			if(-1!=new_fd)
			{
				//打印客户端的ip和port
				char str[IP_LEN];
				inet_ntop(AF_INET, &client_sock.sin_addr, str, sizeof(str));
				printf("connected from %s at PORT %d\n",str,ntohs(client_sock.sin_port));				
				if(1==add_fd_arr(new_fd))
				{
					perror("fd_arr is full,close new_fd\n");
					close(new_fd);//此处比较粗陋，应该关闭久未发生信息交付的fd
				}								
			}
			continue;
		}
		//客户端发来信息
		for(i=0;i<_MAX_SIZE_;++i)
		{
			if(fd_arr[i]!=-1&&FD_ISSET(fd_arr[i],&fds))
			{
				char buf[1024];
				memset(buf,'\0',sizeof(buf));
				ssize_t size=recv(fd_arr[i],buf,sizeof(buf)-1,0);
				//"exit"特殊字符串，表示客户端申请断开链接
				if(size==0 || size==-1 || memcmp(buf,"exit",size)==0)
				{
					printf("remote client fd is %d, close,size is %d,msg is %s\n",fd_arr[i],size,buf);
					FD_CLR(fd_arr[i],&fds);
					close(fd_arr[i]);
					fd_arr[i]=-1;
				}else
				{
					printf("recv: fd:%d,msg:%s\n",fd_arr[i],buf);
					if ((ret = send(fd_arr[i], tcpSendBuf, strlen(tcpSendBuf) + 1, 0)) == -1)
					{
						perror("send error \r\n");
					}					
				}
				osDelay(10);
				continue;
			}
		}
	}	
	close(sock_fd);
	FD_CLR(sock_fd,&fds);
	sock_fd=-1;
	return ;
}

/*tcp服务器任务*/
static void TCPServerTask(void)
{
	//服务端地址信息
	struct sockaddr_in server_sock;
	//客户端地址信息
	struct sockaddr_in client_sock;
	int sin_size;
	struct sockaddr_in *cli_addr;
	//连接Wifi
	WifiConnect(WIFI_SSID,WIFI_PASSWORD);

	//创建socket
	int sock_fd=-1,new_fd=-1;
	if ((sock_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1)
	{
		perror("socket is error\r\n");
		exit(1);
	}
	printf("sock_fd=%d\n",sock_fd);

	bzero(&server_sock, sizeof(server_sock));
	server_sock.sin_family = AF_INET;
	//server_sock.sin_addr.s_addr = htonl(INADDR_ANY);
	inet_pton(AF_INET, "192.168.1.121", &server_sock.sin_addr);
	server_sock.sin_port = htons(_PROT_);

	//调用bind函数绑定socket和地址
	if (bind(sock_fd, (struct sockaddr *)&server_sock, sizeof(struct sockaddr)) == -1)
	{
		perror("bind is error\r\n");
		exit(1);
	}
	//调用listen函数监听(指定port监听)
	if (listen(sock_fd, TCP_BACKLOG) == -1)
	{
		perror("listen is error\r\n");
		exit(1);
	}
	printf("start accept\r\n");

	//打印客户端的ip和port
	char str[IP_LEN];
	printf("server ip %s at PORT %d\n",inet_ntop(AF_INET, &server_sock.sin_addr, str, sizeof(str)),ntohs(server_sock.sin_port));
		

	//调用accept函数从队列中
	while (1)
	{
		sin_size = sizeof(struct sockaddr_in);

		if ((new_fd = accept(sock_fd, (struct sockaddr *)&client_sock, (socklen_t *)&sin_size)) == -1)
		{
			perror("accept");
			continue;
		}
		//打印客户端的ip和port
		char str[IP_LEN];
		printf("connected from %s at PORT %d\n",inet_ntop(AF_INET, &client_sock.sin_addr, str, sizeof(str)),ntohs(client_sock.sin_port));
		//处理目标
		int ret;

		while (1)
		{
			memset(recvbuf,0x0,sizeof(recvbuf));
			if ((ret = recv(new_fd, recvbuf, sizeof(recvbuf), 0)) == -1)
			{
				printf("recv error \r\n");
			}
			printf("recv :%s\r\n", recvbuf);
			if ((ret = send(new_fd, tcpSendBuf, strlen(tcpSendBuf) + 1, 0)) == -1)
			{
				perror("send : ");
			}
			osDelay(100);
		}
		close(new_fd);
	}
}

static void TCPServerDemo(void)
{
	osThreadAttr_t attr;

	attr.name = "TCPServerTask";
	attr.attr_bits = 0U;
	attr.cb_mem = NULL;
	attr.cb_size = 0U;
	attr.stack_mem = NULL;
	attr.stack_size = 1024*4;
	attr.priority = osPriorityNormal;

	if (osThreadNew((osThreadFunc_t)TCPServerTaskWithSelect, NULL, &attr) == NULL)
	{
		printf("[TCPServerDemo] Falied to create TCPServerTask!\n");
	}
}

APP_FEATURE_INIT(TCPServerDemo);